#include "dominion.h"
#include "dominion_helpers.h"
#include "sput/sput.h"
#include <assert.h>
#include <malloc.h>
#include <stdlib.h>
#include <time.h>

struct gameState *testState;

static void test_compare_cards(void)
{
    int lim = randRange(1, 50);
    int rand_changes;
    int rand_index;
    int iter = 10;
    int *lhs, *rhs;
    int i, j;

    if((lhs = malloc(sizeof(int) * lim)) == NULL) {
        return;
    }
    if((rhs = malloc(sizeof(int) * lim)) == NULL) {
        return;
    }

    for(i = 0; i < iter; i++) {
        rand_changes = randRange(1, 50);

        for(j = 0; j < lim; j++) {
            lhs[j] = rhs[j] = randCard();
        }

        sput_fail_unless(compare_cards(lhs, lim, rhs, lim) == 0, "indicate that identical card sets are identical");

        while(rand_changes--) {
            rand_index = randRange(0, lim-1);
            rhs[rand_index] = lhs[rand_index] + 1;
        }

        sput_fail_if(compare_cards(lhs, lim, rhs, lim) == 0, "indicate that different card sets are different");
    }

    free(lhs);
    free(rhs);
}


static void test_draw_card(void)
{
    int numPlayers;
    int supplyPos;
    int player;
    int kc_count = 10;
    int has_deck;
    int hand_count_pre, hand_count_post;
    int deck_count_pre, deck_count_post;
    int discard_count_pre;
    int i;

    int kc[10] = {adventurer, council_room, feast, gardens, mine, remodel,
                  smithy, village, baron, great_hall
                 };

    // iterate through all player counts
    for(numPlayers = 2; numPlayers <= MAX_PLAYERS; numPlayers++) {
        initializeGame(numPlayers, kc, rand(), testState);
        // iterate though all cards
        for(supplyPos = curse; supplyPos <= treasure_map; supplyPos++) {
            //iterate over players
            for(player = 0; player < testState->numPlayers; player++) {
                for(has_deck = 0; has_deck <= 1; has_deck++) {
                    //fill_hand(player, testState, kc, kc_count);
                    //printf("player: %d; has_deck: %d; supplyPos: %d; numPlayers: %d\n", player, has_deck, supplyPos, numPlayers);
                    if(has_deck) {
                        // fill deck
                        for(i = 0; i < testState->deckCount[player]; i++) {
                            testState->deck[player][i] = -1;
                        }
                        for(i = 0; i < kc_count; i++) {
                            testState->deck[player][i] = kc[i];
                        }
                        testState->deckCount[player] = kc_count;

                        // gather pre-draw statistics
                        hand_count_pre = testState->handCount[player];
                        deck_count_pre = testState->deckCount[player];
                        discard_count_pre = testState->discardCount[player];
                    } else {
                        // empty deck and place cards in discard pile
                        testState->deckCount[player] = 0;
                        for(i = 0; i < testState->discardCount[player]; i++) {
                            testState->discard[player][i] = -1;
                        }
                        testState->discardCount[player] = 0;
                        for(i = 0; i < kc_count; i++) {
                            testState->discard[player][i] = kc[i];
                        }
                        testState->discardCount[player] = kc_count;

                        // gather pre-draw statistics
                        hand_count_pre = testState->handCount[player];
                        deck_count_pre = testState->deckCount[player];
                        discard_count_pre = testState->discardCount[player];

                        // make sure that all cards were copied to discard
                        // pile and that deck is empty
                        sput_fail_unless(compare_cards(kc, kc_count, testState->discard[player], testState->discardCount[player]) == 0, "copied known cards to discard pile");
                        sput_fail_unless(discard_count_pre == kc_count, "added correct number of cards to discard pile");
                        sput_fail_unless(deck_count_pre == 0, "removed cards from deck");
                    }

                    // draw a card
                    drawCard(player, testState);

                    // check that values have been altered correctly
                    deck_count_post = testState->deckCount[player];
                    hand_count_post = testState->handCount[player];

                    // make sure that all cards were copied from discard
                    // pile to deck (save the one that was drawn)
                    sput_fail_unless(compare_cards(kc, kc_count, testState->deck[player], deck_count_post) == 1, "copied cards from discard pile to deck");

                    // make sure the numbers add up
                    sput_fail_unless(deck_count_post = hand_count_pre - 1, "removed one card from deck");
                    sput_fail_unless(hand_count_post = hand_count_pre + 1, "placed one card into hand");

                    // add the card from the hand back to the deck to
                    // make sure we drew the right card
                    testState->deck[player][deck_count_post - 1] = testState->hand[player][hand_count_post];
                    testState->deckCount[player]++;
                    testState->handCount[player]--;
                    sput_fail_unless(compare_cards(kc, kc_count, testState->deck[player], testState->deckCount[player]) == 0, "card in hand is card drawn from deck");
                }
            }
        }
    }
}

int main(int argc, char *argv[])
{
    // Seed random number generator
    srand(time(NULL));

    // Initialize game state
    testState = newGame();

    sput_start_testing();
    sput_enter_suite(__FILE__);

    sput_run_test(test_compare_cards);
    sput_run_test(test_draw_card);

    sput_finish_testing();

    free(testState);

    return sput_get_return_value();
}
